package com.xuelang.pipeline.artifact.app.resource.impl;

import cn.hutool.core.util.IdUtil;
import com.xuelang.hiforce.base.tracing.TracingUtils;
import com.xuelang.jadp.client.sys.dto.NgacAssociateDTO;
import com.xuelang.jadp.client.sys.dto.UserInfoDTO;
import com.xuelang.pipeline.artifact.app.resource.ArtifactToolService;
import com.xuelang.pipeline.artifact.app.resource.InstanceRepositoryService;
import com.xuelang.pipeline.artifact.app.resource.converter.ArtifactAttributeDTOToArtifactAttributeConverter;
import com.xuelang.pipeline.artifact.app.resource.converter.ArtifactToolDTOToArtifactToolConverter;
import com.xuelang.pipeline.artifact.app.resource.request.NgacAssociateXO;
import com.xuelang.pipeline.artifact.client.common.constant.Constant;
import com.xuelang.pipeline.artifact.client.common.constant.DictDTO;
import com.xuelang.pipeline.artifact.client.common.request.PageParam;
import com.xuelang.pipeline.artifact.client.common.result.PageRecord;
import com.xuelang.pipeline.artifact.client.common.util.BizAssert;
import com.xuelang.pipeline.artifact.client.common.vo.PipelineProperties;
import com.xuelang.pipeline.artifact.client.resource.dto.*;
import com.xuelang.pipeline.artifact.client.resource.enums.ArtifactToolEnum;
import com.xuelang.pipeline.artifact.domain.resource.ArtifactAttribute;
import com.xuelang.pipeline.artifact.domain.resource.ArtifactTool;
import com.xuelang.pipeline.artifact.domain.resource.ArtifactUser;
import com.xuelang.pipeline.artifact.domain.resource.service.ArtifactAttributeDomainService;
import com.xuelang.pipeline.artifact.domain.resource.service.ArtifactToolDomainService;
import com.xuelang.pipeline.artifact.domain.resource.service.ArtifactUserDomainService;
import com.xuelang.sys.app.sys.NgacManagerService;
import com.xuelang.sys.app.sys.UserManagerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 制品工具服务实现类
 *
 * @author xiwen.zxg
 * @since 2024/5/15
 */
@Slf4j
@Service
public class ArtifactToolServiceImpl implements ArtifactToolService {

    @Autowired
    ArtifactToolDomainService artifactToolDomainService;
    @Autowired
    ArtifactAttributeDomainService artifactAttributeDomainService;
    @Autowired
    ArtifactUserDomainService artifactUserDomainService;
    @Autowired
    InstanceRepositoryService instanceRepositoryService;
    @Autowired
    PipelineProperties pipelineProperties;
    @Autowired
    NgacManagerService ngacManagerService;
    @Autowired
    UserManagerService userManagerService;
    @Autowired
    ArtifactUserServiceImpl artifactUserService;

    @Override
    public PageRecord<ArtifactToolDTO> pageList(PageParam<ArtifactToolQuery> dtoPageParam) {
        ArtifactToolQuery condition = dtoPageParam.getCondition();
        condition = condition == null ? new ArtifactToolQuery() : condition;
        BizAssert.isTrue(ArtifactToolEnum.DataSourceEnum.isValid(condition.getDataSource()), "数据来源不为空，取值范围为TOOL/ARTIFACT.");
        PageRecord<ArtifactTool> pageRecord = artifactToolDomainService.pageList(
                dtoPageParam.getCurrentPage(),
                dtoPageParam.getPageSize(),
                condition);
        return new PageRecord<>(
                pageRecord.getCurrentPage(),
                pageRecord.getPageSize(),
                pageRecord.getTotalCount(),
                pageRecord.getTotalPages(),
                pageRecord.getRecords()
                        .stream()
                        .map(ArtifactToolDTOToArtifactToolConverter.INSTANCE::artifactToolToArtifactToolDTO)
                        .map(this::fullfillCreator)
                        .collect(Collectors.toList()));
    }

    private ArtifactToolDTO fullfillCreator(ArtifactToolDTO artifactToolDTO) {
        String creatorId = artifactToolDTO.getCreatorId();
        UserInfoDTO user = userManagerService.getUserById(creatorId);
        if (Objects.nonNull(user)) {
            artifactToolDTO.setCreator(user.getRealName());
        } else {
            artifactToolDTO.setCreator("[未知用户]");
        }
        return artifactToolDTO;
    }

    @Override
    public List<ArtifactToolDTO> list(ArtifactToolQuery artifactToolQuery) {
        BizAssert.isTrue(ArtifactToolEnum.DataSourceEnum.isValid(artifactToolQuery.getDataSource()), "数据来源不为空，取值范围为TOOL/ARTIFACT.");
        return artifactToolDomainService.list(artifactToolQuery).stream()
                .map(ArtifactToolDTOToArtifactToolConverter.INSTANCE::artifactToolToArtifactToolDTO)
                .map(this::fullfillCreator)
                .collect(Collectors.toList());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(ArtifactToolDTO artifactToolDTO) {
        boolean isArtifact = artifactToolDTO.getDataSource().equals(ArtifactToolEnum.DataSourceEnum.ARTIFACT.name());
        // 校验项目范围
        BizAssert.isTrue(ArtifactToolEnum.OpenScopeEnum.PROJECT.name().equals(artifactToolDTO.getOpenScope()), "制品工具的openScope取值应为PROJECT.");
        // 校验Maven参数
        if (artifactToolDTO.getArtifactType().equals(ArtifactToolEnum.ArtifactTypeEnum.MAVEN.name())) {
            BizAssert.notEmpty(artifactToolDTO.getStorageStrategy(), "Maven仓库的存储策略不能为空.");
            String storageStrategy = Optional.of(artifactToolDTO.getStorageStrategy()).orElse("");
            Arrays.asList(storageStrategy.split(",")).forEach(storageStrategyItem ->
                    BizAssert.isTrue(ArtifactToolEnum.MavenStorageStrategyEnum.isValid(storageStrategyItem), "Maven仓库的存储策略取值范围为RELEASE/SNAPSHOT/MIXED.")
            );
        }
        // 校验工具参数
        if (artifactToolDTO.getDataSource().equals(ArtifactToolEnum.DataSourceEnum.TOOL.name())) {
            BizAssert.isTrue(ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectCode()), "制品工具的projectCode取值应为TOOL_ARTIFACT.");
            BizAssert.isTrue(ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectName()), "制品工具的projectName取值应为TOOL_ARTIFACT.");
            BizAssert.notEmpty(artifactToolDTO.getServerAddress(), "制品工具的serverAddress不能为空.");
        }
        // 校验仓库参数
        if (isArtifact) {
            BizAssert.isFalse(
                    ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectCode())
                            || ArtifactToolEnum.ProjectCodeEnum.INTERNAL_ARTIFACT.name().equals(artifactToolDTO.getProjectCode()),
                    "制品仓库的projectCode取值不能为TOOL_ARTIFACT或INTERNAL_ARTIFACT.");
            BizAssert.isFalse(
                    ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectName())
                            || ArtifactToolEnum.ProjectCodeEnum.INTERNAL_ARTIFACT.name().equals(artifactToolDTO.getProjectName()),
                    "制品仓库的projectName取值不能为TOOL_ARTIFACT或INTERNAL_ARTIFACT.");
        }
        // 同一项目下不能存在相同仓库名称或相同仓库编码
        BizAssert.isFalse(artifactToolDomainService.existsArtifactCodeAndNameByProjectCode(
                        artifactToolDTO.getUuid(),
                        artifactToolDTO.getProjectCode(),
                        artifactToolDTO.getArtifactCode(),
                        artifactToolDTO.getArtifactName()),
                "同一项目下不能存在相同仓库名称或相同仓库编码.");

        String serverAddress = artifactToolDTO.getServerAddress();
        String instanceRepositoryName = artifactToolDTO.getArtifactName();
        // 如果是制品仓库，则需要更新仓库实例名称和拼接服务地址前缀
        if (isArtifact) {
            instanceRepositoryName = artifactToolDTO.getProjectCode() + Constant.DEFAULT_MIDDLE_SEPARATOR +
                    artifactToolDTO.getArtifactCode() + Constant.DEFAULT_MIDDLE_SEPARATOR +
                    artifactToolDTO.getArtifactType();
            // 如果是Maven仓库，则拼接存储策略
            if (artifactToolDTO.getArtifactType().equals(ArtifactToolEnum.ArtifactTypeEnum.MAVEN.name())) {
                // TODO: 这里对应存储策略多选情况有问题，放到8月优化部分功能，多选策略也需要和实例仓库对应。
                instanceRepositoryName = instanceRepositoryName;
            }

            if (artifactToolDTO.getArtifactType().equals(ArtifactToolEnum.ArtifactTypeEnum.DOCKER.name())) {
                // 如果是Docker仓库，设置服务地址=服务地址前缀+仓库名称
                serverAddress = pipelineProperties.getArtifact().getDockerUrlPrefix() + instanceRepositoryName.toLowerCase() + Constant.DEFAULT_URL_PATH_SEPARATOR;
            } else {
                serverAddress = pipelineProperties.getArtifact().getMavenUrlPrefix() + instanceRepositoryName.toLowerCase() + Constant.DEFAULT_URL_PATH_SEPARATOR;
            }
        }

        ArtifactTool tool = ArtifactToolDTOToArtifactToolConverter.INSTANCE.artifactToolDTOToArtifactTool(artifactToolDTO);
        tool.setCreatorId(TracingUtils.getUserId());
        tool.setUpdaterId(TracingUtils.getUserId());
        tool.setUuid(IdUtil.fastSimpleUUID());
        tool.setServerAddress(serverAddress);
        tool.setInstanceRepositoryName(instanceRepositoryName.toLowerCase());

        ArtifactAttribute artifactAttribute = ArtifactAttribute.builder()
                .uuid(tool.getUuid())
                .instanceRepositoryName(instanceRepositoryName.toLowerCase())
                .creatorId(TracingUtils.getUserId())
                .updaterId(TracingUtils.getUserId())
                .webhookId("")
                .webhookType("")
                .profileId("rdc")
                .layoutId("default")
                .serverId("repo-" + tool.getArtifactCode())
                .repoAddress(serverAddress)
                .repoUserName("")//动态判断登录用户权限并返回当前用户的用户名
                .repoPassword("")//动态判断登录用户权限并返回当前用户的密码
                .build();
        boolean isSuccess = artifactToolDomainService.save(tool) && artifactAttributeDomainService.save(artifactAttribute);
        if (isSuccess) {
            // 根据仓库类型去中间件实例创建对应仓库：Nexus3-仓库、Harbor-项目
            if (isArtifact) {
                instanceRepositoryService.createRepository(
                        ArtifactToolEnum.ArtifactTypeEnum.getByKey(artifactToolDTO.getArtifactType()),
                        tool.getInstanceRepositoryName(),
                        tool.getUuid());
            }

            // 将当前用户设置为管理员
            NgacAssociateDTO ngacAssociateDTO = new NgacAssociateDTO();
            ngacAssociateDTO.setObjectId(tool.getUuid());
            ngacAssociateDTO.setObjectType(NgacAssociateXO.ObjectTypeEnum.repository.name());
            ngacAssociateDTO.setTargetUser(TracingUtils.getUserId());
            ngacAssociateDTO.setUserAttribute(NgacAssociateXO.UserAttribute.builder()
                            .userName(TracingUtils.getUserName())
                            .role(NgacAssociateXO.RoleIdEnum.manager.name())
                            .roleName(NgacAssociateXO.RoleIdEnum.manager.getDescription())
                            .build()
                            .toString());
            ngacManagerService.saveNgacAssociate(ngacAssociateDTO);

            // 默认给当前用户的实例仓库也设置一个用户账号
            ArtifactRoleUserDTO artifactRoleUser = ArtifactRoleUserDTO
                    .builder()
                    .artifactUuid(tool.getUuid())
                    .artifactType(artifactToolDTO.getArtifactType())
                    .managerList(Collections.singletonList(ArtifactRoleUserDTO.BusinessUserVO
                            .builder()
                            .userId(TracingUtils.getUserId())
                            .userName(TracingUtils.getUserName())
                            .build()))
                    .build();
            artifactUserService.updateArtifactRoleUser(artifactRoleUser);
        }
        return isSuccess;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(ArtifactToolDTO artifactToolDTO) {
        BizAssert.notNull(artifactToolDTO.getUuid(), "uuid不能为空");
        // 校验项目范围
        BizAssert.isTrue(ArtifactToolEnum.OpenScopeEnum.PROJECT.name().equals(artifactToolDTO.getOpenScope()), "制品工具的openScope取值应为PROJECT.");
        // 校验Maven参数
        if (artifactToolDTO.getArtifactType().equals(ArtifactToolEnum.ArtifactTypeEnum.MAVEN.name())) {
            BizAssert.notEmpty(artifactToolDTO.getStorageStrategy(), "Maven仓库的存储策略不能为空.");
            String storageStrategy = Optional.of(artifactToolDTO.getStorageStrategy()).orElse("");
            Arrays.asList(storageStrategy.split(",")).forEach(storageStrategyItem ->
                    BizAssert.isTrue(ArtifactToolEnum.MavenStorageStrategyEnum.isValid(storageStrategyItem), "Maven仓库的存储策略取值范围为RELEASE/SNAPSHOT/MIXED.")
            );
        }
        // 校验工具参数
        if (artifactToolDTO.getDataSource().equals(ArtifactToolEnum.DataSourceEnum.TOOL.name())) {
            BizAssert.isTrue(ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectCode()), "制品工具的projectCode取值应为TOOL_ARTIFACT.");
            BizAssert.isTrue(ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectName()), "制品工具的projectName取值应为TOOL_ARTIFACT.");
            BizAssert.notEmpty(artifactToolDTO.getServerAddress(), "制品工具的serverAddress不能为空.");
        }
        // 校验仓库参数
        if (artifactToolDTO.getDataSource().equals(ArtifactToolEnum.DataSourceEnum.ARTIFACT.name())) {
            BizAssert.isFalse(
                    ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectCode())
                            || ArtifactToolEnum.ProjectCodeEnum.INTERNAL_ARTIFACT.name().equals(artifactToolDTO.getProjectCode()),
                    "制品仓库的projectCode取值不能为TOOL_ARTIFACT或INTERNAL_ARTIFACT.");
            BizAssert.isFalse(
                    ArtifactToolEnum.ProjectCodeEnum.TOOL_ARTIFACT.name().equals(artifactToolDTO.getProjectName())
                            || ArtifactToolEnum.ProjectCodeEnum.INTERNAL_ARTIFACT.name().equals(artifactToolDTO.getProjectName()),
                    "制品仓库的projectName取值不能为TOOL_ARTIFACT或INTERNAL_ARTIFACT.");
        }
        // 同一项目下不能存在相同仓库名称或相同仓库编码
        BizAssert.isFalse(artifactToolDomainService.existsArtifactCodeAndNameByProjectCode(
                        artifactToolDTO.getUuid(),
                        artifactToolDTO.getProjectCode(),
                        artifactToolDTO.getArtifactCode(),
                        artifactToolDTO.getArtifactName()),
                "同一项目下不能存在相同仓库名称或相同仓库编码.");

        // 提供更新仓库的操作，不提供引起仓库实例变化的信息更新，只允许更新仓库描述和名称
        ArtifactTool tool = ArtifactToolDTOToArtifactToolConverter.INSTANCE.artifactToolDTOToArtifactTool(artifactToolDTO);
        // 制品工具入口时，服务器地址寄制品仓库code不允许更新。制品仓库入口可以更新。
        if (artifactToolDTO.getDataSource().equals(ArtifactToolEnum.DataSourceEnum.ARTIFACT.name())) {
            //服务器地址不允许更新
            tool.setServerAddress(null);
            //制品仓库code不允许更新
            tool.setArtifactCode(null);
        }
        tool.setCreatorId(TracingUtils.getUserId());
        tool.setUpdaterId(TracingUtils.getUserId());
        return artifactToolDomainService.update(tool);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(String toolUuid) {
        BizAssert.notNull(toolUuid, "uuid不能为空");
        ArtifactTool query = artifactToolDomainService.query(toolUuid);
        ArtifactAttribute artifactAttribute = Optional.ofNullable(artifactAttributeDomainService.query(toolUuid)).orElseGet(ArtifactAttribute::new);
        boolean isArtifact = query.getDataSource().equals(ArtifactToolEnum.DataSourceEnum.ARTIFACT.name());
        boolean isSuccess = artifactToolDomainService.delete(toolUuid) && artifactAttributeDomainService.delete(toolUuid);
        if (isSuccess) {
            if (isArtifact) {
                // 根据仓库类型去中间件实例删除对应仓库：Nexus3-仓库、Harbor-项目
                instanceRepositoryService.deleteRepository(query, artifactAttribute);
            }
            // 删除应用权限
            NgacAssociateDTO ngacAssociateDTO = new NgacAssociateDTO();
            ngacAssociateDTO.setObjectId(toolUuid);
            ngacAssociateDTO.setObjectType(NgacAssociateXO.ObjectTypeEnum.repository.name());
            ngacAssociateDTO.setTargetUser(TracingUtils.getUserId());
            ngacManagerService.removeNgacAssociate(ngacAssociateDTO);

        }
        return isSuccess;
    }

    @Override
    public ArtifactToolDTO query(String toolUuid) {
        BizAssert.notNull(toolUuid, "uuid不能为空");
        ArtifactTool tool = artifactToolDomainService.query(toolUuid);
        return ArtifactToolDTOToArtifactToolConverter.INSTANCE.artifactToolToArtifactToolDTO(tool);
    }

    @Override
    public String getArtifactUrlPrefix(String artifactType) {
        BizAssert.isTrue(ArtifactToolEnum.ArtifactTypeEnum.isValid(artifactType), "仓库类型取值范围为:GENERIC/DOCKER/MAVEN/NPM.");
        ArtifactToolEnum.ArtifactTypeEnum artifactTypeEnum = ArtifactToolEnum.ArtifactTypeEnum.getByKey(artifactType);
        switch (artifactTypeEnum) {
            case MAVEN:
                return pipelineProperties.getArtifact().getMavenUrlPrefix();
            case NPM:
                return pipelineProperties.getArtifact().getNpmUrlPrefix();
            case GENERIC:
                return pipelineProperties.getArtifact().getGenericUrlPrefix();
            case DOCKER:
                return pipelineProperties.getArtifact().getDockerUrlPrefix();
            default:
                return "";
        }
    }

    @Override
    public ArtifactAttributeDTO getArtifactGuide(String artifactToolUuid, String userId) {
        ArtifactAttribute query = artifactAttributeDomainService.query(artifactToolUuid);
        BizAssert.notNull(query, "仓库不存在");
        userId = Optional.ofNullable(userId).orElseGet(TracingUtils::getUserId);
        ArtifactUser artifactUser = artifactUserDomainService.queryArtifactUser(userId, artifactToolUuid);
        Optional.ofNullable(artifactUser).ifPresent(user -> {
            query.setRepoPassword(user.getArtifactUserPassword());
            query.setRepoUserName(user.getArtifactUserId());
        });
        return ArtifactAttributeDTOToArtifactAttributeConverter.INSTANCE.artifactAttributeToArtifactAttributeDTO(query);
    }

    @Override
    public PageRecord<PipelineArtifactToolDTO> listArtifactTool() {
//        List<ArtifactToolDTO> collect = artifactToolDomainService.listArtifactTool().stream()
//                .map(ArtifactToolDTOToArtifactToolConverter.INSTANCE::artifactToolToArtifactToolDTO)
//                .map(this::fullfillCreator)
//                .collect(Collectors.toList());
        return new PageRecord<>();
    }

    @Override
    public List<DictDTO> queryBuildTaskDict(String artifactType) {
        ArtifactToolQuery artifactToolQuery = new ArtifactToolQuery();
        //数据源 TOOL
        artifactToolQuery.setDataSource(ArtifactToolEnum.DataSourceEnum.TOOL.name());
        artifactToolQuery.setArtifactType(artifactType);
        List<ArtifactTool> dockerList = artifactToolDomainService.list(artifactToolQuery);
        List<DictDTO> dictList = new ArrayList<>();
        dockerList.forEach(artifactTool -> {
            dictList.add(DictDTO.builder().id(artifactTool.getUuid()).text(artifactTool.getArtifactName()).build());
        });
        return dictList;
    }

}
